/*
 * chombo: Hadoop Map Reduce utility
 * Author: Pranab Ghosh
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */


package org.chombo.stats;

import org.chombo.math.MathUtils;
import org.chombo.util.BasicUtils;

public class ChiSquareDistributionCriticalValues {
	
	//rows are deg of freedom and columns are alpha .050, .025, .020, .010, .005, .002, .001)
	private static double[][] criticalValues = {
		{3.841,5.024,5.412,6.635,7.879,9.550,10.828},
		{5.991,7.378,7.824,9.210,10.597,12.429,13.816},
		{7.815,9.348,9.837,11.345,12.838,14.796,16.266},
		{9.488,11.143,11.668,13.277,14.860,16.924,18.467},
		{11.070,12.833,13.388,15.086,16.750,18.907,20.515},
		{12.592,14.449,15.033,16.812,18.548,20.791,22.458},
		{14.067,16.013,16.622,18.475,20.278,22.601,24.322},
		{15.507,17.535,18.168,20.090,21.955,24.352,26.124},
		{16.919,19.023,19.679,21.666,23.589,26.056,27.877},
		{18.307,20.483,21.161,23.209,25.188,27.722,29.588},
		{19.675,21.920,22.618,24.725,26.757,29.354,31.264},
		{21.026,23.337,24.054,26.217,28.300,30.957,32.909},
		{22.362,24.736,25.472,27.688,29.819,32.535,34.528},
		{23.685,26.119,26.873,29.141,31.319,34.091,36.123},
		{24.996,27.488,28.259,30.578,32.801,35.628,37.697},
		{26.296,28.845,29.633,32.000,34.267,37.146,39.252},
		{27.587,30.191,30.995,33.409,35.718,38.648,40.790},
		{28.869,31.526,32.346,34.805,37.156,40.136,42.312},
		{30.144,32.852,33.687,36.191,38.582,41.610,43.820},
		{31.410,34.170,35.020,37.566,39.997,43.072,45.315},
		{32.671,35.479,36.343,38.932,41.401,44.522,46.797},
		{33.924,36.781,37.659,40.289,42.796,45.962,48.268},
		{35.172,38.076,38.968,41.638,44.181,47.391,49.728},
		{36.415,39.364,40.270,42.980,45.559,48.812,51.179},
		{37.652,40.646,41.566,44.314,46.928,50.223,52.620},
		{38.885,41.923,42.856,45.642,48.290,51.627,54.052},
		{40.113,43.195,44.140,46.963,49.645,53.023,55.476},
		{41.337,44.461,45.419,48.278,50.993,54.411,56.892},
		{42.557,45.722,46.693,49.588,52.336,55.792,58.301},
		{43.773,46.979,47.962,50.892,53.672,57.167,59.703},
		{44.985,48.232,49.226,52.191,55.003,58.536,61.098},
		{46.194,49.480,50.487,53.486,56.328,59.899,62.487},
		{47.400,50.725,51.743,54.776,57.648,61.256,63.870},
		{48.602,51.966,52.995,56.061,58.964,62.608,65.247},
		{49.802,53.203,54.244,57.342,60.275,63.955,66.619},
		{50.998,54.437,55.489,58.619,61.581,65.296,67.985},
		{52.192,55.668,56.730,59.893,62.883,66.633,69.346},
		{53.384,56.896,57.969,61.162,64.181,67.966,70.703},
		{54.572,58.120,59.204,62.428,65.476,69.294,72.055},
		{55.758,59.342,60.436,63.691,66.766,70.618,73.402},
		{56.942,60.561,61.665,64.950,68.053,71.938,74.745},
		{58.124,61.777,62.892,66.206,69.336,73.254,76.084},
		{59.304,62.990,64.116,67.459,70.616,74.566,77.419},
		{60.481,64.201,65.337,68.710,71.893,75.874,78.750},
		{61.656,65.410,66.555,69.957,73.166,77.179,80.077},
		{62.830,66.617,67.771,71.201,74.437,78.481,81.400},
		{64.001,67.821,68.985,72.443,75.704,79.780,82.720},
		{65.171,69.023,70.197,73.683,76.969,81.075,84.037},
		{66.339,70.222,71.406,74.919,78.231,82.367,85.351},
		{67.505,71.420,72.613,76.154,79.490,83.657,86.661},
		{68.669,72.616,73.818,77.386,80.747,84.943,87.968},
		{69.832,73.810,75.021,78.616,82.001,86.227,89.272},
		{70.993,75.002,76.223,79.843,83.253,87.507,90.573},
		{72.153,76.192,77.422,81.069,84.502,88.786,91.872},
		{73.311,77.380,78.619,82.292,85.749,90.061,93.168},
		{74.468,78.567,79.815,83.513,86.994,91.335,94.461},
		{75.624,79.752,81.009,84.733,88.236,92.605,95.751},
		{76.778,80.936,82.201,85.950,89.477,93.874,97.039},
		{77.931,82.117,83.391,87.166,90.715,95.140,98.324},
		{79.082,83.298,84.580,88.379,91.952,96.404,99.607},
		{80.232,84.476,85.767,89.591,93.186,97.665,100.888},
		{81.381,85.654,86.953,90.802,94.419,98.925,102.166},
		{82.529,86.830,88.137,92.010,95.649,100.182,103.442},
		{83.675,88.004,89.320,93.217,96.878,101.437,104.716},
		{84.821,89.177,90.501,94.422,98.105,102.691,105.988},
		{85.965,90.349,91.681,95.626,99.330,103.942,107.258},
		{87.108,91.519,92.860,96.828,100.554,105.192,108.526},
		{88.250,92.689,94.037,98.028,101.776,106.440,109.791},
		{89.391,93.856,95.213,99.228,102.996,107.685,111.055},
		{90.531,95.023,96.388,100.425,104.215,108.929,112.317},
		{91.670,96.189,97.561,101.621,105.432,110.172,113.577},
		{92.808,97.353,98.733,102.816,106.648,111.412,114.835},
		{93.945,98.516,99.904,104.010,107.862,112.651,116.092},
		{95.081,99.678,101.074,105.202,109.074,113.889,117.346},
		{96.217,100.839,102.243,106.393,110.286,115.125,118.599},
		{97.351,101.999,103.410,107.583,111.495,116.359,119.850},
		{98.484,103.158,104.576,108.771,112.704,117.591,121.100},
		{99.617,104.316,105.742,109.958,113.911,118.823,122.348},
		{100.749,105.473,106.906,111.144,115.117,120.052,123.594},
		{101.879,106.629,108.069,112.329,116.321,121.280,124.839},
		{103.010,107.783,109.232,113.512,117.524,122.507,126.083},
		{104.139,108.937,110.393,114.695,118.726,123.733,127.324},
		{105.267,110.090,111.553,115.876,119.927,124.957,128.565},
		{106.395,111.242,112.712,117.057,121.126,126.179,129.804},
		{107.522,112.393,113.871,118.236,122.325,127.401,131.041},
		{108.648,113.544,115.028,119.414,123.522,128.621,132.277},
		{109.773,114.693,116.184,120.591,124.718,129.840,133.512},
		{110.898,115.841,117.340,121.767,125.913,131.057,134.745},
		{112.022,116.989,118.495,122.942,127.106,132.273,135.978},
		{113.145,118.136,119.648,124.116,128.299,133.489,137.208},
		{114.268,119.282,120.801,125.289,129.491,134.702,138.438},
		{115.390,120.427,121.954,126.462,130.681,135.915,139.666},
		{116.511,121.571,123.105,127.633,131.871,137.127,140.893},
		{117.632,122.715,124.255,128.803,133.059,138.337,142.119},
		{118.752,123.858,125.405,129.973,134.247,139.546,143.344},
		{119.871,125.000,126.554,131.141,135.433,140.755,144.567},
		{120.990,126.141,127.702,132.309,136.619,141.962,145.789},
		{122.108,127.282,128.849,133.476,137.803,143.168,147.010},
		{123.225,128.422,129.996,134.642,138.987,144.373,148.230},
		{124.342,129.561,131.142,135.807,140.169,145.577,149.449},
		{125.458,130.700,132.287,136.971,141.351,146.780,150.667},
		{126.574,131.838,133.431,138.134,142.532,147.982,151.884},
		{127.689,132.975,134.575,139.297,143.712,149.183,153.099},
		{128.804,134.111,135.718,140.459,144.891,150.383,154.314},
		{129.918,135.247,136.860,141.620,146.070,151.582,155.528},
		{131.031,136.382,138.002,142.780,147.247,152.780,156.740},
		{132.144,137.517,139.143,143.940,148.424,153.977,157.952},
		{133.257,138.651,140.283,145.099,149.599,155.173,159.162},
		{134.369,139.784,141.423,146.257,150.774,156.369,160.372},
		{135.480,140.917,142.562,147.414,151.948,157.563,161.581},
		{136.591,142.049,143.700,148.571,153.122,158.757,162.788},
		{137.701,143.180,144.838,149.727,154.294,159.950,163.995},
		{138.811,144.311,145.975,150.882,155.466,161.141,165.201},
		{139.921,145.441,147.111,152.037,156.637,162.332,166.406},
		{141.030,146.571,148.247,153.191,157.808,163.523,167.610},
		{142.138,147.700,149.383,154.344,158.977,164.712,168.813},
		{143.246,148.829,150.517,155.496,160.146,165.900,170.016},
		{144.354,149.957,151.652,156.648,161.314,167.088,171.217},
		{145.461,151.084,152.785,157.800,162.481,168.275,172.418},
		{146.567,152.211,153.918,158.950,163.648,169.461,173.617},
		{147.674,153.338,155.051,160.100,164.814,170.647,174.816},
		{148.779,154.464,156.183,161.250,165.980,171.831,176.014},
		{149.885,155.589,157.314,162.398,167.144,173.015,177.212},
		{150.989,156.714,158.445,163.546,168.308,174.198,178.408},
		{152.094,157.839,159.575,164.694,169.471,175.380,179.604},
		{153.198,158.962,160.705,165.841,170.634,176.562,180.799},
		{154.302,160.086,161.834,166.987,171.796,177.743,181.993},
		{155.405,161.209,162.963,168.133,172.957,178.923,183.186},
		{156.508,162.331,164.091,169.278,174.118,180.103,184.379},
		{157.610,163.453,165.219,170.423,175.278,181.282,185.571},
		{158.712,164.575,166.346,171.567,176.438,182.460,186.762},
		{159.814,165.696,167.473,172.711,177.597,183.637,187.953},
		{160.915,166.816,168.600,173.854,178.755,184.814,189.142},
		{162.016,167.936,169.725,174.996,179.913,185.990,190.331},
		{163.116,169.056,170.851,176.138,181.070,187.165,191.520},
		{164.216,170.175,171.976,177.280,182.226,188.340,192.707},
		{165.316,171.294,173.100,178.421,183.382,189.514,193.894},
		{166.415,172.412,174.224,179.561,184.538,190.688,195.080},
		{167.514,173.530,175.348,180.701,185.693,191.861,196.266},
		{168.613,174.648,176.471,181.840,186.847,193.033,197.451},
		{169.711,175.765,177.594,182.979,188.001,194.205,198.635},
		{170.809,176.882,178.716,184.118,189.154,195.376,199.819},
		{171.907,177.998,179.838,185.256,190.306,196.546,201.002},
		{173.004,179.114,180.959,186.393,191.458,197.716,202.184},
		{174.101,180.229,182.080,187.530,192.610,198.885,203.366},
		{175.198,181.344,183.200,188.666,193.761,200.054,204.547},
		{176.294,182.459,184.321,189.802,194.912,201.222,205.727},
		{177.390,183.573,185.440,190.938,196.062,202.390,206.907},
		{178.485,184.687,186.560,192.073,197.211,203.557,208.086},
		{179.581,185.800,187.678,193.208,198.360,204.723,209.265},
		{180.676,186.914,188.797,194.342,199.509,205.889,210.443},
		{181.770,188.026,189.915,195.476,200.657,207.054,211.620},
		{182.865,189.139,191.033,196.609,201.804,208.219,212.797},
		{183.959,190.251,192.150,197.742,202.951,209.383,213.973},
		{185.052,191.362,193.267,198.874,204.098,210.547,215.149},
		{186.146,192.474,194.384,200.006,205.244,211.710,216.324},
		{187.239,193.584,195.500,201.138,206.390,212.873,217.499},
		{188.332,194.695,196.616,202.269,207.535,214.035,218.673},
		{189.424,195.805,197.731,203.400,208.680,215.197,219.846},
		{190.516,196.915,198.846,204.530,209.824,216.358,221.019},
		{191.608,198.025,199.961,205.660,210.968,217.518,222.191},
		{192.700,199.134,201.076,206.790,212.111,218.678,223.363},
		{193.791,200.243,202.190,207.919,213.254,219.838,224.535},
		{194.883,201.351,203.303,209.047,214.396,220.997,225.705},
		{195.973,202.459,204.417,210.176,215.539,222.156,226.876},
		{197.064,203.567,205.530,211.304,216.680,223.314,228.045},
		{198.154,204.675,206.642,212.431,217.821,224.472,229.215},
		{199.244,205.782,207.755,213.558,218.962,225.629,230.383},
		{200.334,206.889,208.867,214.685,220.102,226.786,231.552},
		{201.423,207.995,209.978,215.812,221.242,227.942,232.719},
		{202.513,209.102,211.090,216.938,222.382,229.098,233.887},
		{203.602,210.208,212.201,218.063,223.521,230.253,235.053},
		{204.690,211.313,213.311,219.189,224.660,231.408,236.220},
		{205.779,212.419,214.422,220.314,225.798,232.563,237.385},
		{206.867,213.524,215.532,221.438,226.936,233.717,238.551},
		{207.955,214.628,216.641,222.563,228.074,234.870,239.716},
		{209.042,215.733,217.751,223.687,229.211,236.023,240.880},
		{210.130,216.837,218.860,224.810,230.347,237.176,242.044},
		{211.217,217.941,219.969,225.933,231.484,238.328,243.207},
		{212.304,219.044,221.077,227.056,232.620,239.480,244.370},
		{213.391,220.148,222.185,228.179,233.755,240.632,245.533},
		{214.477,221.251,223.293,229.301,234.891,241.783,246.695},
		{215.563,222.353,224.401,230.423,236.026,242.933,247.857},
		{216.649,223.456,225.508,231.544,237.160,244.084,249.018},
		{217.735,224.558,226.615,232.665,238.294,245.234,250.179},
		{218.820,225.660,227.722,233.786,239.428,246.383,251.339},
		{219.906,226.761,228.828,234.907,240.561,247.532,252.499},
		{220.991,227.863,229.935,236.027,241.694,248.681,253.659},
		{222.076,228.964,231.040,237.147,242.827,249.829,254.818},
		{223.160,230.064,232.146,238.266,243.959,250.977,255.976},
		{224.245,231.165,233.251,239.386,245.091,252.124,257.135},
		{225.329,232.265,234.356,240.505,246.223,253.271,258.292},
		{226.413,233.365,235.461,241.623,247.354,254.418,259.450},
		{227.496,234.465,236.566,242.742,248.485,255.564,260.607},
		{228.580,235.564,237.670,243.860,249.616,256.710,261.763},
		{229.663,236.664,238.774,244.977,250.746,257.855,262.920},
		{230.746,237.763,239.877,246.095,251.876,259.001,264.075},
		{231.829,238.861,240.981,247.212,253.006,260.145,265.231},
		{232.912,239.960,242.084,248.329,254.135,261.290,266.386},
		{233.994,241.058,243.187,249.445,255.264,262.434,267.541},
		{235.077,242.156,244.290,250.561,256.393,263.578,268.695},
		{236.159,243.254,245.392,251.677,257.521,264.721,269.849},
		{237.240,244.351,246.494,252.793,258.649,265.864,271.002},
		{238.322,245.448,247.596,253.908,259.777,267.007,272.155},
		{239.403,246.545,248.698,255.023,260.904,268.149,273.308},
		{240.485,247.642,249.799,256.138,262.031,269.291,274.460},
		{241.566,248.739,250.900,257.253,263.158,270.432,275.612},
		{242.647,249.835,252.001,258.367,264.285,271.574,276.764},
		{243.727,250.931,253.102,259.481,265.411,272.715,277.915},
		{244.808,252.027,254.202,260.595,266.537,273.855,279.066},
		{245.888,253.122,255.302,261.708,267.662,274.995,280.217},
		{246.968,254.218,256.402,262.821,268.788,276.135,281.367},
		{248.048,255.313,257.502,263.934,269.912,277.275,282.517},
		{249.128,256.408,258.601,265.047,271.037,278.414,283.666},
		{250.207,257.503,259.701,266.159,272.162,279.553,284.815},
		{251.286,258.597,260.800,267.271,273.286,280.692,285.964},
		{252.365,259.691,261.898,268.383,274.409,281.830,287.112},
		{253.444,260.785,262.997,269.495,275.533,282.968,288.261},
		{254.523,261.879,264.095,270.606,276.656,284.106,289.408},
		{255.602,262.973,265.193,271.717,277.779,285.243,290.556},
		{256.680,264.066,266.291,272.828,278.902,286.380,291.703},
		{257.758,265.159,267.389,273.939,280.024,287.517,292.850},
		{258.837,266.252,268.486,275.049,281.146,288.653,293.996},
		{259.914,267.345,269.584,276.159,282.268,289.789,295.142},
		{260.992,268.438,270.681,277.269,283.390,290.925,296.288},
		{262.070,269.530,271.777,278.379,284.511,292.061,297.433},
		{263.147,270.622,272.874,279.488,285.632,293.196,298.579},
		{264.224,271.714,273.970,280.597,286.753,294.331,299.723},
		{265.301,272.806,275.066,281.706,287.874,295.465,300.868},
		{266.378,273.898,276.162,282.814,288.994,296.600,302.012},
		{267.455,274.989,277.258,283.923,290.114,297.734,303.156},
		{268.531,276.080,278.354,285.031,291.234,298.867,304.299},
		{269.608,277.171,279.449,286.139,292.353,300.001,305.443},
		{270.684,278.262,280.544,287.247,293.472,301.134,306.586},
		{271.760,279.352,281.639,288.354,294.591,302.267,307.728},
		{272.836,280.443,282.734,289.461,295.710,303.400,308.871},
		{273.911,281.533,283.828,290.568,296.828,304.532,310.013},
		{274.987,282.623,284.922,291.675,297.947,305.664,311.154},
		{276.062,283.713,286.016,292.782,299.065,306.796,312.296},
		{277.138,284.802,287.110,293.888,300.182,307.927,313.437},
		{278.213,285.892,288.204,294.994,301.300,309.058,314.578},
		{279.288,286.981,289.298,296.100,302.417,310.189,315.718},
		{280.362,288.070,290.391,297.206,303.534,311.320,316.859},
		{281.437,289.159,291.484,298.311,304.651,312.450,317.999},
		{282.511,290.248,292.577,299.417,305.767,313.580,319.138},
		{283.586,291.336,293.670,300.522,306.883,314.710,320.278},
		{284.660,292.425,294.762,301.626,307.999,315.840,321.417},
		{285.734,293.513,295.855,302.731,309.115,316.969,322.556},
		{286.808,294.601,296.947,303.835,310.231,318.098,323.694},
		{287.882,295.689,298.039,304.940,311.346,319.227,324.832}		
	};
	private static double[] confIntervals = {.050, .025, .020, .010, .005, .002, .001};
	
	/**
	 * @param degOfFreedom
	 * @param confInterval
	 * @return
	 */
	/**
	 * @param degOfFreedom
	 * @param confIntervalFactor
	 * @param failOnOutOfRange
	 * @return
	 */
	public static double getCriticalPoint(int degOfFreedom, double confIntervalFactor, boolean failOnOutOfRange) {
		double critPoint = -1;
		if (failOnOutOfRange) {
			BasicUtils.assertCondition(degOfFreedom >= 1 && degOfFreedom <= 250, "degree of freedom out of range value " + degOfFreedom);
			BasicUtils.assertCondition(confIntervalFactor >= .001 && confIntervalFactor <= .050, "conf interval factor out of range value " 
			+ BasicUtils.formatDouble(confIntervalFactor, 3));
		}
		
		//force within range
		degOfFreedom = BasicUtils.between(degOfFreedom, 1, 250);
		
		int row = degOfFreedom - 1;
		int col = BasicUtils.indexOfDoubleArray(confIntervals, confIntervalFactor, false);
		if (-1 != col) {
			//direct look up
			critPoint = criticalValues[row][col];
		} else {
			//interpolate
			double[] crValuesForDof = criticalValues[row];
			double[][] crValueTab = new double[confIntervals.length][2];
			for (int i = 0; i < confIntervals.length; ++i) {
				crValueTab[i][0] = confIntervals[i];
				crValueTab[i][1] = crValuesForDof[i];
			}
			critPoint =  MathUtils.linearInterpolate(crValueTab, confIntervalFactor, true);
		}
		return critPoint;
	}
	
}
